/*
类模板
建立一个通用类，类中的成员 数据类型可以不具体制定，用一个虚拟的类型来代表
语法
template<typename T>
类

类模板和函数模板语法相似，在声明模板template后面加类，此类称为类模板
类模板和函数模板的区别：
  类模板没有自动类型推导的使用方式
  类模板在参数模板列表中可以有默认参数

类模板成员函数与普通类中成员函数创建时机的区别
  普通类中的成员函数一开始就可以创建
  类模板中的成员函数在调用时才创建

类模板对象做函数参数，一共有三种方式
  指定传入的类型 - 直接显式对象的数据类型 (比较常使用)
  参数模板化 - 将对象中的参数变为模板进行传递
  整个类模板化 - 将整个对象类型 模板化进行传递

类模板继承
  子类在申明的时候，要指定出父类中T的类型，如果不指定，编译器无法给子类分配内存空间
  如果想要灵活指定父类中T的类型，子类也需要变为类模板

类模板成员函数类外实现
template<class KindType, class NameType>
class Animal
{
    public:
    KindType m_kind;
    NameType m_name;

    // 成员函数类内申明
    Animal(KindType kind, NameType name);
    void show();
};

// 构造函数类外实现
template<class T1, class T2>
Animal<T1, T2>::Animal(T1 kind, T2 name){
    m_kind = kind;
    m_name = name;
}

// 成员函数类外实现
template<class T1, class T2>
void Animal<T1, T2>::show()
{
    cout << "kind: " << m_kind << " name: " << m_name << endl;
}

类模板分文件编写
问题：类模板成员函数的创建是在调用阶段，导致分文件编写时链接不到。
解决：
  方式1：直接包含cpp源文件  // #include "xxxx.cpp"
  方式2： 将申明和实现写道同一个文件中，并改后缀名为hpp, hpp是约定的名称，并不是强制 （常用这种方式） 下面代码中的animal.hpp

类模板与友元
  全局函数 类内 实现 - 直接在类内申明友元即可 推荐这种
  全局函数 类外 实现 - 要提前让编译器知道全局函数的存在
//2、全局函数配合友元  类外实现 - 先做函数模板声明，下方在做函数模板定义，在做友元
template<class T1, class T2> class Person;

//如果声明了函数模板，可以将实现写到后面，否则需要将实现体写到类的前面让编译器提前看到
//template<class T1, class T2> void printPerson2(Person<T1, T2> & p); 

template<class T1, class T2>
void printPerson2(Person<T1, T2> & p)
{
	cout << "类外实现 ---- 姓名： " << p.m_Name << " 年龄：" << p.m_Age << endl;
}

template<class T1, class T2>
class Person
{
	//1、全局函数配合友元   类内实现
	friend void printPerson(Person<T1, T2> & p)
	{
		cout << "姓名： " << p.m_Name << " 年龄：" << p.m_Age << endl;
	}


	//全局函数配合友元  类外实现
	friend void printPerson2<>(Person<T1, T2> & p);

public:

	Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}


private:
	T1 m_Name;
	T2 m_Age;

};

*/

#include <iostream>
#include <string>
#include "animal.hpp"  // 引入定义的类模板 还有一种方式是 .h .cpp 分开 引入的时候 引入.cpp 
using namespace std;

template <class NameType, class AgeType>
class Persion
{
public:
    NameType m_name;
    AgeType m_age;
    Persion(NameType name, AgeType age)
    {
        m_name = name;
        m_age = age;
    }
    void show()
    {
        cout << "name: " << m_name << " age: " << m_age << endl;
    }
};

// 指定默认参数
template <class NameType, class AgeType = int>
class Persion2
{
public:
    NameType m_name;
    AgeType m_age;
    Persion2(NameType name, AgeType age)
    {
        m_name = name;
        m_age = age;
    }
    void show()
    {
        cout << "name: " << m_name << " age: " << m_age << endl;
    }
};

// 类模板对象当参数方式1 指定传入的类型
void printPersion1(Persion<string, int> &p)
{
    cout << "void printPersion1(Persion<string, int> &p)" << endl;
    p.show();
}

// 类模板对象当参数方式2 参数模板化
template <class T1, class T2>
void printPersion2(Persion<T1, T2> &p)
{
    cout << "void printPersion1(Persion<T1, T2> &p)" << endl;
    cout << "T1 type is: " << typeid(T1).name() << endl;
    cout << "T2 type is: " << typeid(T2).name() << endl;
    p.show();
}

// 类模板对象当参数方式3 整个类模板化
template <class T>
void printPersion3(T &p)
{
    cout << "void printPersion1(T &p)" << endl;
    cout << "T type is: " << typeid(T).name() << endl;
    p.show();
}

// 类模板的继承
template <class T>
class Base
{
    T m;
};

// class Son: public Base  // 这样是错误的，编译器无法为子类分配内存 必须要指定出父类中T的类型
class Son : public Base<int>
{
};

// 类模板继承类模板 灵活指定父类中的类型
template <class T1, class T2>
class Son2 : public Base<T2>
{
public:
    Son2()
    {
        cout << "========== this is Son2 ===========" << endl;
        cout << typeid(T1).name() << endl;
        cout << typeid(T2).name() << endl;
    }
};

/*
// 类模板成员函数类外实现
template<class KindType, class NameType>
class Animal
{
    public:
    KindType m_kind;
    NameType m_name;

    // 成员函数类内申明
    Animal(KindType kind, NameType name);
    void show();
};

// 构造函数类外实现
template<class T1, class T2>
Animal<T1, T2>::Animal(T1 kind, T2 name){
    m_kind = kind;
    m_name = name;
}

// 成员函数类外实现
template<class T1, class T2>
void Animal<T1, T2>::show()
{
    cout << "kind: " << m_kind << " name: " << m_name << endl;
}
*/


int main()
{
    // 指定NameType类型为string AgeType为int
    Persion<string, int> p1("monkey", 5000);
    p1.show();

    // 使用默认参数
    Persion2<string> pp1("pig", 1000);
    pp1.show();

    // 类模板对象当参数
    Persion<string, int> p11("sha", 100);
    // 方式1 显式指定参数类型
    printPersion1(p11);

    // 方式2 参数模板化
    printPersion2(p11);

    // 方式3 整个对象模板化
    printPersion3(p11);

    // 普通类继承类模板指定父类T的类型
    Son s;

    // 类模板继承类模板灵感指定父类中T的类型
    Son2<string, int> s2;

    // 成员函数类外实现
    Animal<string, string> dog("Dog", "jinba");
    dog.show();

    return 0;
}